from concurrent.futures import ThreadPoolExecutor, Future
from copy import deepcopy
from dataclasses import dataclass
from pathlib import Path

from PySide6.QtCore import Qt, QObject, Signal
from PySide6.QtGui import QDragEnterEvent
from PySide6.QtWidgets import QApplication, QVBoxLayout, QFileDialog, QSizePolicy
from qfluentwidgets import FluentIcon
from qfluentwidgets.components import PushButton, CaptionLabel, TitleLabel

from src.api.convert_vmd_to_txt import write_vmdtext
from src.api.core.nuthouse01_vmd_parser import read_vmd, write_vmd
from src.api.core.nuthouse01_vmd_struct import VmdBoneFrame, VmdMorphFrame
from src.component.drop_mask import DropMask
from src.message_base_view import MessageBaseView

__version__: str = "0.9.3"


@dataclass
class BoneFrame:
    name: str
    f: int
    pos: list[float]
    rot: list[float]
    phys_off: bool
    interp_x: list[int] = None
    interp_y: list[int] = None
    interp_z: list[int] = None
    interp_r: list[int] = None

    def to_vmdboneframe(self) -> VmdBoneFrame:
        return VmdBoneFrame(
            self.name,
            self.f,
            self.pos,
            self.rot,
            self.phys_off,
            self.interp_x,
            self.interp_y,
            self.interp_z,
            self.interp_r,
        )

    def __eq__(self, other):
        return (
            self.name == other.name
            and self.pos == other.pos
            and self.rot == other.rot
            and self.phys_off == other.phys_off
            and self.interp_x == other.interp_x
            and self.interp_y == other.interp_y
            and self.interp_z == other.interp_z
            and self.interp_r == other.interp_r
        )

    def __lt__(self, other):
        return self.f < other.f


@dataclass
class MorphFrame:
    name: str
    f: int
    val: float

    def to_vmdmorphframe(self) -> VmdMorphFrame:
        return VmdMorphFrame(self.name, self.f, self.val)

    def __eq__(self, other):
        return self.name == other.name and self.val == other.val, self.f == other.f

    def __lt__(self, other):
        return self.f < other.f


def main(vmd_path: Path) -> Path:
    def find_mirror(original, base):
        return base + (base - original)

    output_path = vmd_path.parent / f"{vmd_path.stem}_out.vmd"
    if output_path.exists():
        output_path.unlink()
    data = read_vmd(str(vmd_path))
    if not data:
        raise ValueError("读取文件失败")
    # 将原本的 vmd 文件写入 txt 文件(debug)
    # original_txt_path = vmd_path.name.replace(".vmd", "origin.txt")
    # write_vmdtext(str(original_txt_path), data)
    global_last_bone_frame: int = -1
    for i in data.boneframes:
        if i.f > global_last_bone_frame:
            global_last_bone_frame = i.f
    print(f"last frame: {global_last_bone_frame}")

    processing_list: list[BoneFrame | MorphFrame] = []
    final_bone_frame_list: list[BoneFrame] = []
    for i in data.boneframes:
        bone_frame_dto: BoneFrame = BoneFrame(
            i.name,
            i.f,
            i.pos,
            i.rot,
            i.phys_off,
            i.interp_x,
            i.interp_y,
            i.interp_z,
            i.interp_r,
        )

        if not processing_list:
            processing_list.append(bone_frame_dto)

        elif processing_list[-1].name == bone_frame_dto.name:
            processing_list.append(bone_frame_dto)

        else:
            if len(processing_list) == 1:
                current_frame = processing_list[0]
                current_frame.f = abs(
                    find_mirror(current_frame.f, global_last_bone_frame)
                )
                new_frame = deepcopy(current_frame)
                new_frame.f = find_mirror(current_frame.f, global_last_bone_frame)
                final_bone_frame_list.append(new_frame)
                final_bone_frame_list.extend(processing_list)
                processing_list.clear()
                processing_list.append(bone_frame_dto)
                continue

            # 先加一遍正序的
            final_bone_frame_list.extend(processing_list)
            # 将processing_list中排序后的数据进行处理
            reverse_list: list[BoneFrame] = deepcopy(processing_list)[::-1]
            assert reverse_list == list(reversed(processing_list))
            for index, j in enumerate(reverse_list):
                frame_add = find_mirror(j.f, global_last_bone_frame)
                j.f = frame_add
                final_bone_frame_list.append(j)

            processing_list.clear()
            processing_list.append(bone_frame_dto)

    data.boneframes = [i.to_vmdboneframe() for i in final_bone_frame_list]

    # 接下来处理morph
    global_last_morph_frame: int = max([i.f for i in data.morphframes]) if len(data.morphframes) > 0 else 0
    print(f"last morph frame: {global_last_morph_frame}")

    processing_list.clear()
    final_morph_frame_list: list[MorphFrame] = []
    for i in data.morphframes:
        morph_frame_dto: MorphFrame = MorphFrame(i.name, i.f, i.val)

        if not processing_list:
            processing_list.append(morph_frame_dto)

        elif processing_list[-1].name == morph_frame_dto.name:
            processing_list.append(morph_frame_dto)

        else:
            if len(processing_list) == 1:
                current_frame = processing_list[0]
                current_frame.f = abs(find_mirror(current_frame.f, 0))
                final_morph_frame_list.extend(processing_list)
                processing_list.clear()
                processing_list.append(morph_frame_dto)
                continue

            # 先处理之前的
            final_morph_frame_list.extend(processing_list)
            # 将processing_list中排序后的数据进行处理
            reverse_list: list[MorphFrame] = deepcopy(processing_list)[::-1]
            for index, j in enumerate(reverse_list):
                # if index == 0:
                # continue
                frame_add = find_mirror(j.f, global_last_morph_frame)
                j.f = frame_add
                final_morph_frame_list.append(j)

            processing_list.clear()
            processing_list.append(morph_frame_dto)

    data.morphframes = [i.to_vmdmorphframe() for i in final_morph_frame_list]

    # 关闭所有的IK
    # data.ikdispframes = []
    # for i in data.ikdispframes:
    #     i.disp = False
    #     for each in i.ikbones:
    #         each.enable = False

    write_vmd(str(output_path), data)
    write_vmdtext(str(output_path.with_suffix(".txt")), data)
    return output_path


class Worker(QObject):
    finished = Signal(Path)
    error = Signal(str)

    def run(self, vmd_path: Path):
        try:
            result = main(vmd_path)
            return result
        except Exception as e:
            return e


class GUI(MessageBaseView):
    def __init__(self):
        super().__init__()
        self.resize(630, 380)
        self.setAcceptDrops(True)
        self.setWindowTitle("VMDReversed")

        self._thread_pool = ThreadPoolExecutor(max_workers=1)

        self._worker = Worker()
        self._worker.finished.connect(self._on_finished)
        self._worker.error.connect(self._on_error)
        self._is_running: bool = False

        self.dialog_mask = DropMask(self, "拖入文件", FluentIcon.ZIP_FOLDER)
        self.dialog_mask.droped_file_url.connect(lambda x: self._process(Path(x)))
        self.dialog_mask.hide()
        self._title_label = TitleLabel()
        self._title_label.setText("将文件拖入窗口或者点击下方选择一个VMD文件")
        self._title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self._title_label.setSizePolicy(
            QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
        )
        self._caption_label = CaptionLabel()
        self._caption_label.setText(
            "该软件会将VMD文件中的动作镜像复制一份，实现动作的无缝循环"
        )
        self._caption_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self._caption_label.setSizePolicy(
            QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
        )
        self._version_label = CaptionLabel()
        self._version_label.setText(f"Version: {__version__}")
        self._version_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self._version_label.setSizePolicy(
            QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
        )
        self._select_button = PushButton()
        self._select_button.setText("选择vmd文件")
        self._select_button.clicked.connect(self.select_file)

        self._main_layout = QVBoxLayout()
        self._main_layout.addWidget(self._title_label)
        self._main_layout.addWidget(self._caption_label)
        self._main_layout.addWidget(self._version_label)
        self._main_layout.addWidget(self._select_button)
        self.setLayout(self._main_layout)

    def select_file(self):
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择一个VMD文件", "", "VMD Files (*.vmd)"
        )
        if file_path:
            if not self._is_running:
                self._is_running = True
                self.show_state_tooltip("运行中", "正在处理，请稍等")

                self._worker.finished.connect(self._on_finished)
                self._thread_pool.submit(self._worker.run, Path(file_path))
            else:
                self.show_error_infobar("错误", "请等待当前任务完成")

    def _process(self, vmd_file_path: Path):
        if not self._is_running:
            self._is_running = True
            self.show_state_tooltip("运行中", "正在处理，请稍等")

            future = self._thread_pool.submit(self._worker.run, vmd_file_path)
            future.add_done_callback(self._handle_future)
        else:
            self.show_error_infobar("错误", "请等待当前任务完成")
        return None

    def _handle_future(self, future: Future):
        self._is_running = False
        try:
            result = future.result()
            if isinstance(result, Exception):
                self._worker.error.emit(str(result))
            else:
                self._worker.finished.emit(result)
        except Exception as e:
            self._worker.error.emit(str(e))

    def _on_finished(self, result: Path):
        self._is_running = False
        self.show_success_infobar(f"处理完成，文件保存在{result}", "success")
        self.finish_state_tooltip("完成", f"文件已处理完成:{result}")

    def _on_error(self, error: str):
        self._is_running = False
        self.show_error_infobar("错误", error, is_closable=True)
        self.finish_state_tooltip("错误", error)

    def dropEvent(self, e):
        self.dialog_mask.dropEvent(e)

    def dragEnterEvent(self, e: QDragEnterEvent):
        if e.mimeData().hasText():
            self.dialog_mask.show()

    # 窗体大小发生改变触发事件
    def resizeEvent(self, event):
        self.resize_mask()

    def resize_mask(self) -> None:
        self.dialog_mask.resize(self.width(), self.height())


if __name__ == "__main__":
    # main(Path(r"H:\SteamLibrary\steamapps\workshop\content\1968650\3410318836\com3d2 pole_out.vmd"))
    app = QApplication([])
    mainwindow = GUI()
    mainwindow.show()
    app.exec()
